package com.bmf.module.rbac.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bmf.constant.AdminResultEnum;
import com.bmf.core.result.PageResponse;
import com.bmf.core.result.R;
import com.bmf.module.rbac.entity.*;
import com.bmf.module.rbac.mapper.RbacUserMapper;
import com.bmf.module.rbac.service.OrganizationService;
import com.bmf.module.rbac.service.RbacRoleService;
import com.bmf.module.rbac.service.RbacUserRoleService;
import com.bmf.module.rbac.service.RbacUserService;
import com.bmf.module.rbac.vo.*;
import com.bmf.module.testQuestions.entity.SmallQuestion;
import com.bmf.module.testQuestions.vo.QuestionResultVo;
import com.bmf.utils.BeanCopyUtils;
import com.bmf.utils.JwtUtil;
import com.bmf.utils.RedisCache;
import com.bmf.utils.SecurityUtils;
import org.aspectj.weaver.ast.Var;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * (RbacUser)表服务实现类
 *
 * @author makejava
 * @since 2022-12-27 15:33:18
 */
@Service("rbacUserService")
public class RbacUserServiceImpl extends ServiceImpl<RbacUserMapper, RbacUser> implements RbacUserService {

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private static final Long MILLIS_MINUTE_TEN = 20 * 60 * 1000L;

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RbacUserRoleService rbacUserRoleService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private RbacRoleService rbacRoleService;
    /**
     * 登录
     * @param user
     * @return
     */
    @Override
    public R<UserLoginVo> login(RbacUser user) {
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword());
        Authentication authenticate = authenticationManager.authenticate(token);
        //判断是否认证通过
        if(Objects.isNull(authenticate)){
            throw new RuntimeException("用户名或密码错误");
        }
        //获取userid 生成token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        //把用户信息存入redis
        redisCache.setCacheObject("login:"+userId,loginUser,30, TimeUnit.MINUTES);
        refreshToken(loginUser);
        //把token和userinfo封装 返回
        //把User转换成UserInfoVo
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(loginUser.getUser(), UserInfoVo.class);
        UserLoginVo vo = new UserLoginVo(jwt,userInfoVo);
        return R.success(vo);
    }
    /**
     * 刷新令牌有效期
     *
     * @param loginUser 登录信息
     */
    public void refreshToken(LoginUser loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + 30 * MILLIS_MINUTE);
        // 根据uuid将loginUser缓存
        String userKey = "login:"+loginUser.getUser().getId();
        redisCache.setCacheObject(userKey, loginUser, 30, TimeUnit.MINUTES);
    }
    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     *
     * @return 令牌
     */
    public void verifyToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= MILLIS_MINUTE_TEN)
        {
            refreshToken(loginUser);
        }
    }
    @Override
    public R<Void> logout() {
        //获取token 解析获取userid
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        //获取userid
        Integer userId = loginUser.getUser().getId();
        //删除redis中的用户信息
        redisCache.deleteObject("login:"+userId);
        return R.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer insert(RbacUserCreateVo rbacUserCreateVo) {
        RbacUser rbacUser = BeanCopyUtils.copyBean(rbacUserCreateVo, RbacUser.class);
        rbacUser.setPassword(passwordEncoder.encode(rbacUser.getPassword()));
        boolean save = save(rbacUser);
        AdminResultEnum.CREATE_EXCEPTION.assertIsTrue(save);
        List<Integer> roleIds = rbacUserCreateVo.getRoleIds();
        if(CollUtil.isEmpty(roleIds)){
            return rbacUser.getId();
        }
        List<RbacUserRole> rbacUserRoles = roleIds.stream().map(s -> new RbacUserRole(null, rbacUser.getId(), s)).collect(Collectors.toList());
        boolean b = rbacUserRoleService.saveBatch(rbacUserRoles);
        AdminResultEnum.CREATE_EXCEPTION.assertIsTrue(b);
        return rbacUser.getId();
    }

    @Override
    public void delete(List<Integer> ids) {
        boolean b = removeByIds(ids);
        AdminResultEnum.DELETE_EXCEPTION.assertIsTrue(b);
        LambdaQueryWrapper<RbacUserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(RbacUserRole::getUserid, ids);
        boolean remove = rbacUserRoleService.remove(wrapper);
        AdminResultEnum.DELETE_EXCEPTION.assertIsTrue(remove);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(RbacUserUpdateVo rbacUserUpdateVo) {
        RbacUser rbacUser = BeanCopyUtils.copyBean(rbacUserUpdateVo, RbacUser.class);
        updateById(rbacUser);
        List<Integer> roleIds = rbacUserUpdateVo.getRoleIds();
        if(CollUtil.isEmpty(roleIds)){
            LambdaQueryWrapper<RbacUserRole> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(RbacUserRole::getUserid, rbacUserUpdateVo.getId());
            rbacUserRoleService.remove(wrapper);
            return;
        }
        //todo 角色修改
        LambdaQueryWrapper<RbacUserRole> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RbacUserRole::getUserid, rbacUserUpdateVo.getId());
        List<RbacUserRole> list = rbacUserRoleService.list(wrapper);
        if(CollUtil.isEmpty(list)&&CollUtil.isEmpty(rbacUserUpdateVo.getRoleIds())){
            return;
        }
        List<Integer> roleids = list.stream().map(RbacUserRole::getRoleid).collect(Collectors.toList());
        Collections.sort(roleids);
        Collections.sort(roleIds);
        if(!roleIds.toString().equals(roleids.toString())){
            LambdaQueryWrapper<RbacUserRole> wrapper2 = new LambdaQueryWrapper<>();
            wrapper2.eq(RbacUserRole::getUserid, rbacUserUpdateVo.getId());
            rbacUserRoleService.remove(wrapper2);
            //重新添加
            List<RbacUserRole> rbacUserRoles = roleIds.stream().map(s -> new RbacUserRole(null, rbacUserUpdateVo.getId(), s)).collect(Collectors.toList());
            boolean b = rbacUserRoleService.saveBatch(rbacUserRoles);
            AdminResultEnum.UPDATE_EXCEPTION.assertIsTrue(b);
        }
    }

    @Override
    public RbacUserResultVo select(Integer id) {
        RbacUser byId = getById(id);
        if(byId==null){
            return null;
        }
        RbacUserResultVo rbacUserResultVo = BeanCopyUtils.copyBean(byId, RbacUserResultVo.class);
        if(ObjectUtil.isNotEmpty(byId.getOrganizationId())){
            Organization organization = organizationService.getById(byId.getOrganizationId());
            if(organization!=null){
                rbacUserResultVo.setOrganizationResultVo(BeanCopyUtils.copyBean(organization,OrganizationResultVo.class));
            }
        }
        LambdaQueryWrapper<RbacUserRole> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.eq(RbacUserRole::getUserid,id);
        List<RbacUserRole> list = rbacUserRoleService.list(wrapper1);
        if(CollUtil.isNotEmpty(list)){
            List<Integer> roleids = list.stream().map(RbacUserRole::getRoleid).collect(Collectors.toList());
            if(CollUtil.isNotEmpty(roleids)){
                List<RbacRole> rbacRoles = rbacRoleService.listByIds(roleids);
                if(CollUtil.isNotEmpty(rbacRoles)){
                    rbacUserResultVo.setRoleIds(rbacRoles.stream().map(RbacRole::getId).collect(Collectors.toList()));
                }
            }
        }
        
        return rbacUserResultVo;
    }

    @Override
    public PageResponse<RbacUserResultVo> selectAll(RbacUserPageVo rbacUserPageVo) {
        LambdaQueryWrapper<RbacUser> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(rbacUserPageVo.getUsername())) {
            wrapper.like(RbacUser::getUsername, rbacUserPageVo.getUsername());
        }
        if (StrUtil.isNotBlank(rbacUserPageVo.getName())) {
            wrapper.like(RbacUser::getName, rbacUserPageVo.getName());
        }
        if (StrUtil.isNotBlank(rbacUserPageVo.getStatus())) {
            wrapper.eq(RbacUser::getStatus, rbacUserPageVo.getStatus());
        }
        if(ObjectUtil.isNotEmpty(rbacUserPageVo.getSex())){
            wrapper.eq(RbacUser::getSex, rbacUserPageVo.getSex());
        }
        if(ObjectUtil.isNotEmpty(rbacUserPageVo.getOrganizationId())){
            wrapper.eq(RbacUser::getOrganizationId, rbacUserPageVo.getOrganizationId());
        }
        //1.判断角色
        //1.1总管理员能查询所有 1.2管理员能查询根节点下所有
        if(SecurityUtils.getUserId()!=1){
            if(SecurityUtils.getLoginUser().getUser().getOrganizationId()!=null){
                List<Integer> list = organizationService.getIdsByRootId(SecurityUtils.getLoginUser().getUser().getOrganizationId());
                if(CollUtil.isEmpty(list)){
                    return new PageResponse<>(null, 0,rbacUserPageVo.getPageNo(),rbacUserPageVo.getPageSize());
                }
                wrapper.in(RbacUser::getOrganizationId, list);
            }
        }
        Page<RbacUser> page = new Page(rbacUserPageVo.getPageNo(),rbacUserPageVo.getPageSize());
        page(page,wrapper);
        List<RbacUser> records = page.getRecords();
        if(CollUtil.isEmpty(records)){
            return new PageResponse<>(null, page.getTotal(),rbacUserPageVo.getPageNo(),rbacUserPageVo.getPageSize());
        }
        List<RbacUserResultVo> rbacUserResultVos = BeanCopyUtils.copyBeanList(records, RbacUserResultVo.class);
        List<Integer> oid = records.stream().map(s -> s.getOrganizationId()).collect(Collectors.toList());
        if(CollUtil.isNotEmpty(oid)){
            List<Organization> organizations = organizationService.listByIds(oid);
            if(CollUtil.isNotEmpty(organizations)){
                Map<Integer, Organization> collect = organizations.stream().collect(Collectors.toMap(Organization::getId, Function.identity()));
                rbacUserResultVos.forEach(s->{
                    Organization organization = collect.get(s.getOrganizationId());
                    if(ObjectUtil.isNotEmpty(organization)){
                        s.setOrganizationResultVo(BeanCopyUtils.copyBean(organization, OrganizationResultVo.class));
                    }
                });
            }
        }
        List<Integer> userids = records.stream().map(s -> s.getId()).collect(Collectors.toList());
        LambdaQueryWrapper<RbacUserRole> wrapper1 = new LambdaQueryWrapper<>();
        wrapper1.in(RbacUserRole::getUserid,userids);
        List<RbacUserRole> list = rbacUserRoleService.list(wrapper1);
        if(CollUtil.isNotEmpty(list)){
            Map<Integer, List<RbacUserRole>> collect1 = list.stream().collect(Collectors.groupingBy(RbacUserRole::getUserid));
            List<Integer> roleids = list.stream().map(RbacUserRole::getRoleid).collect(Collectors.toList());
            if(CollUtil.isNotEmpty(roleids)){
                List<RbacRole> rbacRoles = rbacRoleService.listByIds(roleids);
                if(CollUtil.isNotEmpty(rbacRoles)){
                    Map<Integer, RbacRole> collect = rbacRoles.stream().collect(Collectors.toMap(RbacRole::getId, Function.identity()));
                    rbacUserResultVos.forEach(s->{
                        List<RbacUserRole> rbacUserRoles = collect1.get(s.getId());
                        if(CollUtil.isNotEmpty(rbacUserRoles)){
                            List<RbacRole> collect2 = rbacUserRoles.stream().map(k -> collect.get(k.getRoleid())).filter(k -> k != null).distinct().collect(Collectors.toList());
                            List<RbacRoleResultVo> rbacRoleResultVos = BeanCopyUtils.copyBeanList(collect2, RbacRoleResultVo.class);
                            s.setRoles(rbacRoleResultVos);
                        }
                    });
                }
            }
        }
        return new PageResponse<>(rbacUserResultVos, page.getTotal(),rbacUserPageVo.getPageNo(),rbacUserPageVo.getPageSize());
    }

    @Override
    public List<RbacUser> selectByOrganizationId(Integer id) {
        LambdaQueryWrapper<RbacUser> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RbacUser::getOrganizationId, id);
        return list(wrapper);
    }
}
